/*
 * Copyright (C) 2015-2017 Alibaba Group Holding Limited
 */

/*********************
 *      INCLUDES
 *********************/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <soc.h>
#include <aos/aos.h>
#include <aos/kernel.h>
#include "app_main.h"
#include "oled.h"
#include "oledfont.h"

#include "board_config.h"
#include "drv/gpio_pin.h"
#include <drv/pin.h>

#define TAG "APP_OLED"
#define code

#define SPI // 4-wire Serial

static uint8_t SSD1306_Buffer[SSD1306_BUFFER_SIZE];
static SSD1306_t SSD1306;


extern aos_queue_t weather_queue;


#ifdef SPI // 4-wire Serial
csi_gpio_pin_t pin_clk;
csi_gpio_pin_t pin_mosi;
csi_gpio_pin_t pin_cs;
csi_gpio_pin_t pin_miso;

static void oled_pinmux_init()
{
    csi_pin_set_mux(PA28, PIN_FUNC_GPIO); //clk
    csi_pin_set_mux(PA29, PIN_FUNC_GPIO); //mosi
    csi_pin_set_mux(PA27, PIN_FUNC_GPIO); //cs
    csi_pin_set_mux(PA30, PIN_FUNC_GPIO); //miso
}

static void oled_gpio_init()
{
    //
    csi_gpio_pin_init(&pin_clk, PA28);
    csi_gpio_pin_dir(&pin_clk, GPIO_DIRECTION_OUTPUT);
    csi_gpio_pin_init(&pin_mosi, PA29);
    csi_gpio_pin_dir(&pin_mosi, GPIO_DIRECTION_OUTPUT);
    csi_gpio_pin_init(&pin_cs, PA27);
    csi_gpio_pin_dir(&pin_cs, GPIO_DIRECTION_OUTPUT);
    csi_gpio_pin_init(&pin_miso, PA30); //dc
    csi_gpio_pin_dir(&pin_miso, GPIO_DIRECTION_OUTPUT);
}

static void lcd_cs(uint8_t d)
{
    if (d == 1) {
        csi_gpio_pin_write(&pin_cs, GPIO_PIN_HIGH);
    } else {
        csi_gpio_pin_write(&pin_cs, GPIO_PIN_LOW);
    }
}

static void lcd_dc(uint8_t d)
{
    if (d == 1) {
        csi_gpio_pin_write(&pin_miso, GPIO_PIN_HIGH);
    } else {
        csi_gpio_pin_write(&pin_miso, GPIO_PIN_LOW);
    }
}

static void lcd_sclk(uint8_t d)
{
    if (d == 1) {
        csi_gpio_pin_write(&pin_clk, GPIO_PIN_HIGH);
    } else {
        csi_gpio_pin_write(&pin_clk, GPIO_PIN_LOW);
    }
}

static void lcd_sdin(uint8_t d)
{
    if (d == 1) {
        csi_gpio_pin_write(&pin_mosi, GPIO_PIN_HIGH);
    } else {
        csi_gpio_pin_write(&pin_mosi, GPIO_PIN_LOW);
    }
}

static void Write_Command(unsigned char Data)
{
    unsigned char i;

    lcd_cs(0);
    lcd_dc(0);
    for (i = 0; i < 8; i++) {
        lcd_sclk(0);
        lcd_sdin((Data & 0x80) >> 7);
        Data = Data << 1;
        lcd_sclk(1);
    }
    lcd_dc(1);
    lcd_cs(1);
}


static void WriteDatas(uint8_t* buffer, size_t buff_size)
{
    lcd_cs(0);
    lcd_dc(1);
    for(size_t j=0;j<buff_size;j++)
    {
        for (int i = 0; i < 8; i++) {
            lcd_sclk(0);
            lcd_sdin((buffer[j] & 0x80) >> 7);
            buffer[j] = buffer[j] << 1;
            lcd_sclk(1);
        }
    }
    lcd_dc(1);
    lcd_cs(1);
}

#endif


void Set_Addressing_Mode(unsigned char d)
{
    Write_Command(0x20); // Set Memory Addressing Mode
    Write_Command(d);    //   Default => 0x02
                         //     0x00 => Horizontal Addressing Mode
                         //     0x01 => Vertical Addressing Mode
                         //     0x02 => Page Addressing Mode
}

void Set_Start_Line(unsigned char d)
{
    Write_Command(0x40 | d); // Set Display Start Line
                             //   Default => 0x40 (0x00)
}

void Set_Contrast_Control(unsigned char d)
{
    Write_Command(0x81); // Set Contrast Control for Bank 0
    Write_Command(d);    //   Default => 0x7F
}

void Set_Segment_Remap(unsigned char d)
{
    Write_Command(d); // Set Segment Re-Map
                      //   Default => 0xA0
                      //     0xA0 => Column Address 0 Mapped to SEG0
                      //     0xA1 => Column Address 0 Mapped to SEG127
}

void Set_Entire_Display(unsigned char d)
{
    Write_Command(d); // Set Entire Display On / Off
                      //   Default => 0xA4
                      //     0xA4 => Normal Display
                      //     0xA5 => Entire Display On
}

void Set_Inverse_Display(unsigned char d)
{
    Write_Command(d); // Set Inverse Display On/Off
                      //   Default => 0xA6
                      //     0xA6 => Normal Display
                      //     0xA7 => Inverse Display On
}

void Set_Multiplex_Ratio(unsigned char d)
{
    Write_Command(0xA8); // Set Multiplex Ratio
    Write_Command(d);    //   Default => 0x3F (1/64 Duty)
}

void Set_Display_On_Off(unsigned char d)
{
    Write_Command(d); // Set Display On/Off
                      //   Default => 0xAE
                      //     0xAE => Display Off
                      //     0xAF => Display On
}

void Set_Common_Remap(unsigned char d)
{
    Write_Command(d); // Set COM Output Scan Direction
                      //   Default => 0xC0
                      //     0xC0 => Scan from COM0 to 63
                      //     0xC8 => Scan from COM63 to 0
}

void Set_Display_Offset(unsigned char d)
{
    Write_Command(0xD3); // Set Display Offset
    Write_Command(d);    //   Default => 0x00
}

void Set_Display_Clock(unsigned char d)
{
    Write_Command(0xD5); // Set Display Clock Divide Ratio / Oscillator Frequency
    Write_Command(d);    //   Default => 0x70
                         //     D[3:0] => Display Clock Divider
                         //     D[7:4] => Oscillator Frequency
}

void Set_Low_Power(unsigned char d)
{
    Write_Command(0xD8); // Set Low Power Display Mode
    Write_Command(d);    //   Default => 0x04 (Normal Power Mode)
}

void Set_Precharge_Period(unsigned char d)
{
    Write_Command(0xD9); // Set Pre-Charge Period
    Write_Command(d); //   Default => 0x22 (2 Display Clocks [Phase 2] / 2 Display Clocks [Phase 1])
                      //     D[3:0] => Phase 1 Period in 1~15 Display Clocks
                      //     D[7:4] => Phase 2 Period in 1~15 Display Clocks
}

void Set_Common_Config(unsigned char d)
{
    Write_Command(0xDA); // Set COM Pins Hardware Configuration
    Write_Command(d);    //   Default => 0x12
                         //     Alternative COM Pin Configuration
                         //     Disable COM Left/Right Re-Map
}

void Set_VCOMH(unsigned char d)
{
    Write_Command(0xDB); // Set VCOMH Deselect Level
    Write_Command(d);    //   Default => 0x34 (0.78*VCC)
}


void Set_Command_Lock(unsigned char d)
{
    Write_Command(0xFD); // Set Command Lock
    Write_Command(d);    //   Default => 0x12
                         //     0x12 => Driver IC interface is unlocked from entering command.
                         //     0x16 => All Commands are locked except 0xFD.
}

// Write the screenbuffer with changed to the screen
void ssd1306_UpdateScreen(void) 
{
    for(uint8_t i = 0; i < SSD1306_HEIGHT/8; i++) 
    {
        Write_Command(0xB0 + i); // Set the current RAM page address.
        Write_Command(0x00);
        Write_Command(0x10);
        WriteDatas(&SSD1306_Buffer[SSD1306_WIDTH*i], SSD1306_WIDTH);
    }
}

void ssd1306_DrawPixel(uint8_t x, uint8_t y, SSD1306_COLOR color) 
{
    if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT)
        return;
    
    // Check if pixel should be inverted
    if(SSD1306.Inverted)
    {
        color = (SSD1306_COLOR)!color;
    }
    
    // Draw in the right color
    if(color == White)
    {
        SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);
    } else
    {
        SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));
    }
}

char ssd1306_WriteChar(char ch, FontDef Font, SSD1306_COLOR color) 
{
    uint32_t i, b, j;

    if (ch < 32 || ch > 126)
        return 0;

    if (SSD1306_WIDTH < (SSD1306.CurrentX + Font.FontWidth) ||
        SSD1306_HEIGHT < (SSD1306.CurrentY + Font.FontHeight))
    {
        return 0;
    }
    
    for(i = 0; i < Font.FontHeight; i++)
    {
        b = Font.data[(ch - 32) * Font.FontHeight + i];
        for(j = 0; j < Font.FontWidth; j++)
        {
            if((b << j) & 0x8000)
            {
                ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR) color);
            } else
            {
                ssd1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR)!color);
            }
        }
    }

    SSD1306.CurrentX += Font.FontWidth;
    return ch;
}

// Write full string to screenbuffer
char ssd1306_WriteString(char* str, FontDef Font, SSD1306_COLOR color)
{
    while (*str)
    {
        if (ssd1306_WriteChar(*str, Font, color) != *str)
        {
            return *str;
        }
        str++;
    }
    return *str;
}

// Fill the whole screen with the given color
void ssd1306_Fill(SSD1306_COLOR color) 
{
    /* Set memory */
    uint32_t i;

    for(i = 0; i < sizeof(SSD1306_Buffer); i++) 
    {
        SSD1306_Buffer[i] = (color == Black) ? 0x00 : 0xFF;
    }
}

void ssd1306_SetCursor(uint8_t x, uint8_t y)
{
    SSD1306.CurrentX = x;
    SSD1306.CurrentY = y;
}


static void oled_initialize()
{
    Set_Command_Lock(0x12);           // Unlock Driver IC (0x12/0x16)
    Set_Display_On_Off(0xAE);         // Display Off (0xAE/0xAF)
    Set_Display_Clock(0xA0);          // Set Clock as 116 Frames/Sec
    Set_Multiplex_Ratio(0x3F);        // 1/64 Duty (0x0F~0x3F)
    Set_Display_Offset(0x00);         // Shift Mapping RAM Counter (0x00~0x3F)
    Set_Start_Line(0x00);             // Set Mapping RAM Display Start Line (0x00~0x3F)
    Set_Low_Power(0x04);              // Set Normal Power Mode (0x04/0x05)
    Set_Addressing_Mode(0x02);        // Set Page Addressing Mode (0x00/0x01/0x02)
    Set_Segment_Remap(0xA1);          // Set SEG/Column Mapping (0xA0/0xA1)
    Set_Common_Remap(0xC8);           // Set COM/Row Scan Direction (0xC0/0xC8)
    Set_Common_Config(0x12);          // Set Alternative Configuration (0x02/0x12)
    Set_Contrast_Control(0xBF);       // Set SEG Output Current
    Set_Precharge_Period(0x82);       // Set Pre-Charge as 8 Clocks & Discharge as 2 Clocks
    Set_VCOMH(0x34);                  // Set VCOM Deselect Level
    Set_Entire_Display(0xA4);         // Disable Entire Display On (0xA4/0xA5)
    Set_Inverse_Display(0xA6);        // Disable Inverse Display On (0xA6/0xA7)

    ssd1306_Fill(Black); // Clear Screen

    Set_Display_On_Off(0xAF); // Display On (0xAE/0xAF)
}


static void oled_start_show(SSD1306_COLOR color)
{
    int screencolor = 0, wordcolor = 0;

    screencolor = color;
    wordcolor = screencolor?0:1;

    ssd1306_Fill(screencolor);
    ssd1306_SetCursor(2, 0);
    ssd1306_WriteString("YoC-CH2601", Font_11x18, wordcolor);
    ssd1306_SetCursor(2, 18);
    ssd1306_WriteString("Hello World", Font_11x18, wordcolor);
    ssd1306_SetCursor(2, 18+18);
    ssd1306_WriteString("Sam XIE", Font_7x10, wordcolor);
    ssd1306_SetCursor(2, 18+18+10);
    ssd1306_WriteString("QQ:958361561", Font_6x8, wordcolor);
    ssd1306_SetCursor(2, 18+18+10+8);
    ssd1306_WriteString("TEL:17503114086", Font_6x8, wordcolor);
    ssd1306_UpdateScreen();
}

static void oled_show(void)
{
	for(int i = 0;i<3;i++)
	{
		oled_start_show(White);
		aos_msleep(400);
		oled_start_show(Black);
		aos_msleep(400);
		oled_start_show(White);
		aos_msleep(400);
		oled_start_show(Black);
		aos_msleep(400);
	}
    
}

void ssd1306_screen_update(SSD1306_COLOR screencolor, SSD1306_COLOR wordcolor, const app_weather_t *weather_data)
{
    char message_temp[24] = "";
    char message_time[24] = "";
    char message_info[24] = "";
    sprintf(message_temp, "%s", weather_data->temperature);
    sprintf(message_time, "%s", weather_data->time);
    sprintf(message_info, "%s", weather_data->info);

    ssd1306_Fill(screencolor);
    ssd1306_SetCursor(2, 0);
    ssd1306_WriteString("YoC-CH2601", Font_11x18, wordcolor);
    ssd1306_SetCursor(2, 18);
    ssd1306_WriteString("Sam XIE", Font_7x10, wordcolor);
    ssd1306_SetCursor(2, 18+10);
    ssd1306_WriteString("City:SZ-BaoAn", Font_6x8, wordcolor);
    ssd1306_SetCursor(2, 18+10+8);
    ssd1306_WriteString(message_temp, Font_6x8, wordcolor);// 时间不够，其实可以接收串口（wifi to ab32vg1）的UTC时间，这里转换成年月日，时分
    ssd1306_SetCursor(2, 18+10+8+8);
    ssd1306_WriteString(message_info, Font_6x8, wordcolor);
    ssd1306_SetCursor(2, 18+10+8+8+8);
    ssd1306_WriteString(message_time, Font_6x8, wordcolor);
    ssd1306_UpdateScreen();
}

static void oled_task(void *arg)
{
    app_weather_t recvmsgbuffer;

    unsigned int weather_data_size = 0;

    while(1){
		LOGD(TAG, "oled task alive");
        if(aos_queue_recv(&weather_queue, AOS_WAIT_FOREVER, &recvmsgbuffer, &weather_data_size) != 0){
            LOGD(TAG, "recv msg failed\r\n ");
        }else{
            LOGD(TAG, "recv msg temperature:%s\r\n", recvmsgbuffer.temperature);
            LOGD(TAG, "recv msg time:%s\r\n", recvmsgbuffer.time);
            LOGD(TAG, "recv msg info:%s\r\n", recvmsgbuffer.info);
            ssd1306_screen_update(White, Black, &recvmsgbuffer);
        }
    }
}

static void oled_start(void)
{
    aos_task_t task;
    if (aos_task_new_ext(&task, "oled", oled_task, NULL, 1024, AOS_DEFAULT_APP_PRI + 4) != 0) {
        return;
    }
    LOGD(TAG, "\r\n creat task:button successful\r\n ");
}


void app_oled_init(void)
{
    oled_pinmux_init();
    oled_gpio_init();
    oled_initialize();
    oled_show();
	oled_start();
}



